import { UUID } from "./common";
import { MessageInfo, MessageType, MessageTypeSub, sendMessage } from "./message";

/**
 * 统一接口格式, 根据自己业务修改
 */
export interface FetchResult<T = unknown> {
    /** 200 成功, 其他失败 */
    code?: 200 | -1;
    message?: string;
    data?: T;
}

/** fetch 响应类型 */
type ResponseType = 'json' | 'txt'

export interface FetchAPI<F = never, T = unknown> {
    (body?: F): Promise<FetchResult<T>>
}

const _fetch = async function <F extends object, T>(href: string, data?: F, init?: RequestInit, raw?: ResponseType) {
    const { headers = {}, ...rest } = init || {}
    const loc = new URL(href)
    return fetch(href, {
        credentials: 'include',
        headers: {
            'Content-Type': 'application/json',
            origin: loc.origin,
            ...headers,
        },
        method: data ? 'POST' : 'GET',
        cache: 'no-cache',
        body: data && JSON.stringify(data),
        ...rest,
    }).then(async (res) => {
        switch (raw || 'json') {
        case 'json':
            if (res.status != 200) {
                return { code: -1 } as T
            }
            return res.json() as T
        case 'txt':
            return res.text() as T
        }
    })
}

export interface FetchPack<F> {
    href: string;
    data: F;
    init: RequestInit;
    raw: ResponseType;
}
export const FETCH_EVENT_TYPE = 'FETCH_EVENT_TYPE:'
const _fetch_agent = function <F extends object, T>(href: string, data?: F, init?: RequestInit, raw?: ResponseType) {
    return new Promise<T>(function (resolve, reject) {
        const uuid = UUID()
        sendMessage<FetchPack<F>>({
            type: MessageType.FETCH,
            uuid,
            data: { href, data, init, raw }
        })
        let finished = false
        const ev_id = FETCH_EVENT_TYPE + uuid
        const listener = function (e: Event) {
            finished = true
            resolve(e['detail'])
        }
        addEventListener(ev_id, listener)
        setTimeout(function () {
            removeEventListener(ev_id, listener)
            if (!finished) {
                reject('f2e-crx-creator: _fetch_agent timeout!')
            }
        }, __CONFIG__.FETCH_TIMEOUT || 15000)
    })
}

export const Fetch = __SCOPE__ === RunScope.Content ? _fetch_agent : _fetch

export const SSE_EVENT_TYPE = 'SSE_EVENT_TYPE:'
export const beginSSE = function (url: string, options?: {
    onmessage?: (e: MessageEvent) => void,
    onerror?: (e: Event) => void,
    onclose?: () => void,
    onopen?: () => void,
}) {
    if (__SCOPE__ !== RunScope.Content) {
        return
    }
    const uuid = UUID()
    const key = SSE_EVENT_TYPE + uuid
    const listener = function listener (e) {
        const detail: MessageInfo = e['detail']
        switch(detail.subType) {
        case MessageTypeSub.SSE_OPEN:
            options?.onopen?.()
            break
        case MessageTypeSub.SSE_MESSAGE:
            options?.onmessage?.({ data: detail.data } as MessageEvent)
            break
        case MessageTypeSub.SSE_ERROR:
            options?.onerror?.(detail.data as Event)
            removeEventListener(key, listener)
            break
        case MessageTypeSub.SSE_CLOSE:
            options?.onclose?.()
            removeEventListener(key, listener)
            break
        }
    }
    addEventListener(key, listener)
    sendMessage<string>({
        type: MessageType.SSE,
        uuid,
        data: url,
    })
}